//
//  SharedPointer.h
//  CPPLearning
//
//  Created by Xiao  Feng on 23/6/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#ifndef CLASSES_GAME_UTIL_SHAREPOINTER_H_
#define CLASSES_GAME_UTIL_SHAREPOINTER_H_

class ReferencCounter
{
public:
    ReferencCounter() : m_referenceCount(0) {}
    ~ReferencCounter() {}
    
public:
    void retain() { m_referenceCount++; } 
    void release() { m_referenceCount--; }
    bool isZero() { return m_referenceCount == 0; }
    
private:
    int m_referenceCount;
}; 

template<typename T>
class SharedPointer
{
public:
    typedef T DataType;
    
    SharedPointer(DataType* pData);
    ~SharedPointer();
    
    SharedPointer(const SharedPointer<T>& other);
    SharedPointer& operator=(const SharedPointer<T>& other);
    
    DataType& operator*();
    DataType* operator->();
    DataType* get();
    
private:
    void retain();
    void release();
    
private:
    DataType* m_pData;
    ReferencCounter* m_pReferenceCounter;
};

template<typename T>
SharedPointer<T>::SharedPointer(DataType* pData)
: m_pData(pData)
, m_pReferenceCounter(NULL)
{
    m_pReferenceCounter = new ReferencCounter();
    retain();
}

template<typename T>
SharedPointer<T>::~SharedPointer()
{
    release();
}

template<typename T>
SharedPointer<T>::SharedPointer(const SharedPointer<T>& other)
: m_pData(other.m_pData)
, m_pReferenceCounter(other.m_pReferenceCounter)
{
    retain();
}

template<typename T>
SharedPointer<T>& SharedPointer<T>::operator=(const SharedPointer<T>& other)
{
    release();
    
    m_pData = other.m_pData;
    m_pReferenceCounter = other.m_pReferenceCounter;
    
    retain();
}

template<typename T>
typename SharedPointer<T>::DataType& SharedPointer<T>::operator*()
{
    return *m_pData;
}

template<typename T>
typename SharedPointer<T>::DataType* SharedPointer<T>::operator->()
{
    return m_pData;
}

template<typename T>
typename SharedPointer<T>::DataType* SharedPointer<T>::get()
{
    return m_pData;
}

template<typename T>
void SharedPointer<T>::retain()
{
    m_pReferenceCounter->retain();
}

template<typename T>
void SharedPointer<T>::release()
{
    m_pReferenceCounter->release();
    if (m_pReferenceCounter->isZero())
    {
        delete m_pData;
        delete m_pReferenceCounter;
    }
}

#endif //CLASSES_GAME_UTIL_SHAREPOINTER_H_
